/*
 * Copyright The OpenTelemetry Authors
 * SPDX-License-Identifier: Apache-2.0
 */

package com.xiashitech.agent.instrumentation.guava.inst;


import com.xiashitech.agent.cache.status.CacheStatus;
import com.xiashitech.agent.config.StartConfig;
import com.xiashitech.agent.dto.UserRequest;
import com.xiashitech.agent.instrumentation.bridge.Java8Bridge;
import com.xiashitech.agent.instrumentation.helper.calldepth.XSDubboLt27AbstractPeerSendCallDepth;
import com.xiashitech.agent.instrumentation.utils.BaggageUtil;
import com.xiashitech.agent.utils.DurationPrinter;
import com.xiashitech.agent.utils.JsonUtil;
import io.opentelemetry.javaagent.bootstrap.CallDepth;
import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation;
import io.opentelemetry.javaagent.extension.instrumentation.TypeTransformer;
import io.opentelemetry.javaagent.shaded.io.opentelemetry.context.Context;
import io.opentelemetry.javaagent.shaded.io.opentelemetry.context.Scope;
import net.bytebuddy.asm.Advice;
import net.bytebuddy.description.type.TypeDescription;
import net.bytebuddy.matcher.ElementMatcher;

import java.lang.reflect.InvocationTargetException;

import static io.opentelemetry.javaagent.bootstrap.Java8BytecodeBridge.currentContext;
import static net.bytebuddy.matcher.ElementMatchers.*;

public class XSGuavaLocalCacheInstrumentation implements TypeInstrumentation {
    // private static final Logger logger = Logger.getLogger(AbstractPeerSendInstrumentation.class.getName());

    @Override
    public ElementMatcher<TypeDescription> typeMatcher() {
        // return implementsInterface(named("com.alibaba.nacos.shaded.io.grpc.netty.shaded.io.netty.channel.ChannelOutboundInvoker"));
        return named("com.google.common.cache.LocalCache");
    }


    @Override
    public void transform(TypeTransformer transformer) {
        transformer.applyAdviceToMethod(
                isMethod()
                        .and(named("get"))
                        /* .and(takesArgument(0, named("java.lang.Object")))
                         .and(takesArguments(1))*/
                        .and(takesArgument(0, named("java.lang.Object")))
                        .and(takesArguments(1))
                        .and(isPublic()),
                XSGuavaLocalCacheInstrumentation.class.getName() + "$CacheGetAdvice");
        System.out.println("###guava111");
    }

    @SuppressWarnings("unused")
    public static class CacheGetAdvice {

        @Advice.OnMethodEnter(suppress = Throwable.class)
        public static void onEnter(
                @Advice.Argument(value = 0, readOnly = false) Object response,
                //  @Advice.This Object servletInvocableHandlerMethod,
                @Advice.Local("otelContext") Context context,
                @Advice.Local("otelCallDepth") CallDepth callDepth,
                @Advice.Local("otelScope") Scope scope,
                @Advice.Local("user") UserRequest userRequest
        ) throws Exception {
            DurationPrinter durationPrinter = new DurationPrinter("XSGuavaLocalCacheInstrumentation");
            System.out.println("###guava003");
            try {
                if (CacheStatus.cannotRun()) {
                    System.out.println("###guava Cache not run  excute ");
                    return;
                }
                callDepth = CallDepth.forClass(XSDubboLt27AbstractPeerSendCallDepth.class);
                if (callDepth.getAndIncrement() > 0) {
                    System.out.println("###guava callDepth003  excute " + callDepth.decrementAndGet());
                    return;
                }
                if (response == null) {
                    return;
                }
                userRequest = UserRequest.builder().build();
                Context parentContext = currentContext();
                parentContext = BaggageUtil.syncBaggage().build().storeInContext(parentContext);
                scope = parentContext.makeCurrent();
            } catch (Throwable e) {
                StartConfig.throwableStackPrinter(e);
            }
            durationPrinter.printElapseDuration("CacheGetAdvice b");

        }

        @Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class)
        public static void onExit(
                @Advice.Argument(value = 0) Object response,
                @Advice.Thrown Throwable throwable,
                @Advice.Local("otelCallDepth") CallDepth callDepth,
                @Advice.Local("otelContext") Context context,
                @Advice.Local("otelScope") Scope scope,
                @Advice.Local("user") UserRequest userRequest

        ) throws InvocationTargetException, NoSuchMethodException, IllegalAccessException {
            DurationPrinter durationPrinter = new DurationPrinter("XSGuavaLocalCacheInstrumentation");

            try {
                if (CacheStatus.cannotRun()) {
                    System.out.println("###Cache not Run 006");

                    return;
                }
                if (callDepth.decrementAndGet() > 0) {
                    System.out.println("###callDepth004  excute" + callDepth.decrementAndGet());
                    return;
                }
                if (scope == null) {
                    System.out.println("###mmm5-0");
                    return;
                }
                scope.close();
                Java8Bridge.currentForTraceContextVars()
                        .set("xs.api.responseParams", JsonUtil.convertObjectToString(response));
                System.out.println("###response exit :" + response.toString());
                System.out.println("###mmm5");
            } catch (Throwable e) {
                e.printStackTrace();
            } finally {
                System.out.println("###guava OnMethodExit builder excute response " + JsonUtil.convertObjectToString(response));
                //logger.log(XSLevel.DEBUG,"###debug 009");
            }
            durationPrinter.printElapseDuration("CacheGetAdvice e");
        }
    }
}

